package org.example.gateway.filter;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import lombok.extern.slf4j.Slf4j;
import org.example.gateway.common.BackHttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;

/**
 * @description: IP防暴暴刷（限制单个客户端IP在最近X分钟内请求注册接⼝不不能超过Y次）
 * @author: e
 * @createDate: 2021/5/30
 */
@Slf4j
@Component
@RefreshScope
public class RequestLimitGlobalFilter implements GlobalFilter, Ordered {

    /***
     * 注册防刷创建缓存，默认60000毫秒过期(1分钟)
     **/
    private TimedCache<String, Integer> REQUEST_REGISTER_IP_CACHE;

    /**
     * 1分钟内只能获取一次验证码
     */
    private TimedCache<String, Integer> REQUEST_GET_CODE_IP_CACHE;
    //注册的uri
    private final String registerUri = "/api/user/register";
    //获取验证码
    private final String createCodeUri = "/api/code/create";

    //每分钟内最大请求的次数
    @Value("${geteway.max.request}")
    private int maxRquest;

    //分钟
    @Value("${geteway.minute}")
    private int minute;


    /***
     * 过滤器核心方法
     **/
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //1
        // 1.1 获取IP地址
        String clientIp = Objects.requireNonNull(request.getRemoteAddress()).getHostString();
        // 1.2 获取uri
        String path = request.getURI().getPath();
        //1分钟内只能获取一次验证码
        if (path.startsWith(createCodeUri)) {
            if (REQUEST_GET_CODE_IP_CACHE == null) {
                if (minute == 0) {
                    minute = 1;
                }
                REQUEST_GET_CODE_IP_CACHE = CacheUtil.newTimedCache((long) 60 * 1000);
            }
            //当前IP地址是否再缓存中
            if (REQUEST_GET_CODE_IP_CACHE.containsKey(clientIp)) {
                Integer integer = REQUEST_GET_CODE_IP_CACHE.get(clientIp);
                if (integer == 1) {
                    log.error("一分钟内只能获取一次验证码 .");
                    return response.writeWith(Mono.just(BackHttpStatus.wrap(response, HttpStatus.NOT_ACCEPTABLE)));
                }
            } else {
                REQUEST_GET_CODE_IP_CACHE.put(clientIp, 1);
            }
        }
        //ip 防爆，限制单个客户端IP 在1分钟内注册超过100次时返回错误信息
        if (path.startsWith(registerUri)) {
            if (REQUEST_REGISTER_IP_CACHE == null) {
                if (minute == 0) {
                    minute = 1;
                }
                REQUEST_REGISTER_IP_CACHE = CacheUtil.newTimedCache((long) minute * 60 * 1000);
            }
            //当前IP地址是否再缓存中
            if (REQUEST_REGISTER_IP_CACHE.containsKey(clientIp)) {
                //一分钟内请求的数量
                Integer requestsNum = REQUEST_REGISTER_IP_CACHE.get(clientIp);
                if (requestsNum >= maxRquest) {
                    // 决绝访问，返回状态码和消息
                    log.debug("=====>IP:" + clientIp + "  " + minute + "分钟内请求数量超过最大数 " + maxRquest + " 次！");
                    return response.writeWith(Mono.just(BackHttpStatus.wrap(response, HttpStatus.FORBIDDEN)));
                }
                REQUEST_REGISTER_IP_CACHE.put(clientIp, requestsNum + 1);
            } else {
                //不在则加1
                REQUEST_REGISTER_IP_CACHE.put(clientIp, 1);
            }
        }
        return chain.filter(exchange);
    }

    /***
     * 返回值表示当前过滤器的顺序（优先级），数值越小，优先级越高
     **/
    @Override
    public int getOrder() {
        return 0;
    }
}
